

#ifndef SAFE_QUEUE_H_
#define SAFE_QUEUE_H_


#pragma once



#include <memory>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <thread>
#include <queue>

namespace SafeQueue
{
    template <typename T>
    class SafeQueue
    {
    private:
        mutable std::mutex mMutex;
        std::queue<T> mQueue;
        std::condition_variable mCond;

    public:
        SafeQueue() {}
        SafeQueue(SafeQueue const &other)
        {
            std::lock_guard<std::mutex> lk(other.mMutex);
            mQueue = other.mQueue;
        }

        void push(T new_value)
        {
            std::lock_guard<std::mutex> lk(mMutex);
            mQueue.push(new_value);
            mCond.notify_one();
        }

        void waitAndPop(T &value)
        {
            std::unique_lock<std::mutex> lk(mMutex);
            mCond.wait(lk, [this]()
                       { return !mQueue.empty(); });
            value = mQueue.front();
            mQueue.pop();
        }

        std::shared_ptr<T> waitAndPop()
        {
            std::unique_lock<std::mutex> lk(mMutex);
            mCond.wait(lk, [this]()
                       { return !mQueue.empty(); });
            std::shared_ptr<T> res(std::make_shared<T>(mQueue.front()));
            mQueue.pop();
            return res;
        }

        bool tryPop(T &value)
        {
            std::lock_guard<std::mutex> lk(mMutex);
            if (mQueue.empty())
                return false;
            value = mQueue.front();
            mQueue.pop();
            return true;
        }

        std::shared_ptr<T> tryPop()
        {
            std::lock_guard<std::mutex> lk(mMutex);
            if (mQueue.empty())
                return std::shared_ptr<T>();
            std::shared_ptr<T> res(std::make_shared<T>(mQueue.front()));
            mQueue.pop();
            return res;
        }

        bool empty() const
        {
            std::lock_guard<std::mutex> lk(mMutex);
            return mQueue.empty();
        }
        int mqueueSize()
        {
            return mQueue.size();
        }
    };
}


#endif
// SafeQueue<std::string> sq;

// void thread1Fun(std::shared_ptr<SafeQueue::SafeQueue<std::string>> sq)
// // void thread1Fun()
// {
//     int queue_size = 0;

//     while (1)
//     {
//         queue_size = sq->mqueueSize();
//         if (queue_size < 40)
//         {
//             sq->push("abc");
//             std::this_thread::sleep_for(std::chrono::milliseconds(50));

//             std::cout << "in thread1Fun push " << sq->mqueueSize() << std::endl;
//         }
//         else if (queue_size > 40)
//         {
//             std::this_thread::sleep_for(std::chrono::milliseconds(4000));
//         }
//         queue_size = sq->mqueueSize();
//         std::cout << "thread1Fun queue_size = " << queue_size << std::endl;
//     }

//     return;
// }

// void thread2Fun(std::shared_ptr<SafeQueue::SafeQueue<std::string>> sq)

// // void thread2Fun()
// {
//     std::string popDat;
//     while (1)
//     {
//         if (sq->mqueueSize())
//         {
//             do
//             {
//                 sq->tryPop(popDat);
//                 std::cout << "thread2Fun  sQueue size" << sq->mqueueSize() << " : " << popDat << std::endl;
//             } while (sq->mqueueSize());
//         }
//         std::this_thread::sleep_for(std::chrono::milliseconds(2000));
//     }

//     return;
// }

// int main()
// {
    // std::shared_ptr<SafeQueue::SafeQueue<std::string>> sq;// = nullptr;
    // //auto
    // sq = std::make_shared<SafeQueue::SafeQueue<std::string>>(); // makeshared  fan hui shared _Ptr
//     std::thread t1(thread1Fun, sq);
//     std::thread t2(thread2Fun, sq);
//     // std::thread t1(thread1Fun);
//     // std::thread t2(thread2Fun);

//     t1.join();
//     t2.join();
//     return 0;
// }
